package com.lws.framework.dao;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;

import org.hibernate.LockMode;
import org.hibernate.Query;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

public class HibernateBaseDAO<T extends Serializable> extends HibernateDaoSupport implements BaseDao<T> {

    private static final Logger log = LoggerFactory.getLogger(HibernateBaseDAO.class);

    private Class<T> entityClass;
    private String entityClassName;

    @Autowired
    public void setTemplate(HibernateTemplate hibernateTemplate) {
        super.setHibernateTemplate(hibernateTemplate);
    }

    @SuppressWarnings("unchecked")
    public HibernateBaseDAO() {
        Type type = getClass().getGenericSuperclass();
        if (type instanceof ParameterizedType) {
            ParameterizedType parameterizedType = (ParameterizedType) type;
            this.entityClass = (Class<T>) parameterizedType.getActualTypeArguments()[0];
            this.entityClassName = entityClass.getSimpleName();
        }
    }

    @Override
    public void save(T transientInstance) {
        log.debug("saving " + entityClassName + " instance");
        try {
            getHibernateTemplate().save(transientInstance);
            log.debug("save successful");
        } catch (RuntimeException re) {
            log.error("save failed", re);
            throw re;
        }
    }

    @Override
    public void delete(Long id) {
        log.debug("deleting " + entityClassName + " instance");
        try {
            T result = findById(id);
            if (result != null) {
                delete(result);
                log.debug("delete successful");
            } else {
                log.debug("not find " + id + " delete failed");
            }
        } catch (RuntimeException re) {
            log.error("delete failed", re);
            throw re;
        }
    }

    @Override
    public void delete(T persistentInstance) {
        log.debug("deleting " + entityClassName + " instance");
        try {
            getHibernateTemplate().delete(persistentInstance);
            log.debug("delete successful");
        } catch (RuntimeException re) {
            log.error("delete failed", re);
            throw re;
        }
    }

    @Override
    public T findById(Long id) {
        log.debug("getting " + entityClassName + " instance with id: " + id);
        try {
            T instance = getHibernateTemplate().get(entityClass, id);
            return instance;
        } catch (RuntimeException re) {
            log.error("get failed", re);
            throw re;
        }
    }

    @Override
    public List<T> findByExample(T instance) {
        log.debug("finding " + entityClassName + " instance by example");
        try {
            @SuppressWarnings("unchecked")
            List<T> results = getHibernateTemplate().findByExample(instance);
            log.debug("find by example successful, result size: " + results.size());
            return results;
        } catch (RuntimeException re) {
            log.error("find by example failed", re);
            throw re;
        }
    }

    @Override
    public List<T> findByProperty(String propertyName, Object value) {
        log.debug("finding " + entityClassName + " instance with property: " + propertyName + ", value: " + value);
        try {
            String queryString = "from " + entityClassName + " as model where model." + propertyName + "= ?";
            @SuppressWarnings("unchecked")
            List<T> results = getHibernateTemplate().find(queryString, value);
            return results;
        } catch (RuntimeException re) {
            log.error("find by property name failed", re);
            throw re;
        }
    }

    @Override
    public List<T> findAll() {
        log.debug("finding all " + entityClassName + " instances");
        try {
            String queryString = "from " + entityClassName;
            @SuppressWarnings("unchecked")
            List<T> results = getHibernateTemplate().find(queryString);
            return results;
        } catch (RuntimeException re) {
            log.error("find all failed", re);
            throw re;
        }
    }

    @Override
    public void update(T detachedInstance) {
        log.debug("updating " + entityClassName + " instance");
        try {
            getHibernateTemplate().update(detachedInstance);
            log.debug("update successful");
        } catch (RuntimeException re) {
            log.error("update failed", re);
            throw re;
        }
    }

    @Override
    public Page<T> findByPage(Integer pageNo, Integer pageSize) {
        log.debug("findByPage " + entityClassName + " instance");
        if (pageNo < 1) {
            throw new RuntimeException("页码不能小于1");
        }
        if (pageSize < 0) {
            throw new RuntimeException("页量不能小于0");
        }

        Query query = getSession().createQuery("From " + entityClassName);
        query.setFirstResult((pageNo - 1) * pageSize).setMaxResults(pageSize);

        @SuppressWarnings("unchecked")
        List<T> result = query.list();
        Page<T> page = new Page<T>(pageNo, pageSize, result);
        return page;
    }

    @Override
    public void executeHQL(String hql, Object[] values) {
        Query query = getSession().createQuery(hql);

        if (values != null) {
            for (int i = 0; i < values.length; i++) {
                query.setParameter(i, values[i]);
            }
        }

        int result = query.executeUpdate();
        log.debug("影响行数：" + result);
    }

    @Deprecated
    public T merge(T detachedInstance) {
        log.debug("merging " + entityClassName + " instance");
        try {
            T result = getHibernateTemplate().merge(detachedInstance);
            log.debug("merge successful");
            return result;
        } catch (RuntimeException re) {
            log.error("merge failed", re);
            throw re;
        }
    }

    @Deprecated
    public void attachDirty(T instance) {
        log.debug("attaching dirty " + entityClassName + " instance");
        try {
            getHibernateTemplate().saveOrUpdate(instance);
            log.debug("attach successful");
        } catch (RuntimeException re) {
            log.error("attach failed", re);
            throw re;
        }
    }

    @Deprecated
    public void attachClean(T instance) {
        log.debug("attaching clean " + entityClassName + " instance");
        try {
            getHibernateTemplate().lock(instance, LockMode.NONE);
            log.debug("attach successful");
        } catch (RuntimeException re) {
            log.error("attach failed", re);
            throw re;
        }
    }

}